/**
 * 
 */
package it.polimi.cg16.client.rmi;

import it.polimi.cg16.client.NetAdapter;
import it.polimi.cg16.exceptions.InactivePlayerException;
import it.polimi.cg16.net.RmiComm;
import it.polimi.cg16.net.RmiConnection;

import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * @author Andrea
 * 
 */
public class RMINetAdapter implements NetAdapter {
    private static final Logger LOGGER = Logger.getGlobal();
    private static final String CONTEXT = "context";
    private static final String ME = "client";
    private static final String SERVER = "server";

    private String username;

    // Network attributes
    private RmiComm communication;
    private int number;

    public RMINetAdapter(String username) {
        this.username = username;
    }

    /**
     * @See it.polimi.cg16.client.netAdapter#connect();
     */
    public void connect() {
        try {
            run();
        } catch (RemoteException | NotBoundException e) {
            LOGGER.log(Level.FINE, CONTEXT, e);
        } catch (InactivePlayerException e) {
            LOGGER.log(Level.FINE, CONTEXT, e);
        }
    }

    /**
     * @See it.polimi.cg16.client.netAdapter#closeConnection();
     */
    @Override
    public void closeConnection() {
        // Not implemented for RMI
    }

    private void run() throws RemoteException, NotBoundException, InactivePlayerException {
        Registry reg = LocateRegistry.getRegistry("localhost");
        RmiConnection rmiConnStub = (RmiConnection) reg.lookup("RmiStart");

        number = rmiConnStub.createAdapter();

        communication = (RmiComm) reg.lookup("RmiCommunication" + number);
        sendMessage(username);

        number = rmiConnStub.enterGame(number);
    }

    /**
     * @see it.polimi.cg16.client.NetAdapter#getUsername()
     */
    @Override
    public String getUsername() {
        return username;
    }

    /**
     * @See it.polimi.cg16.client.netAdapter#readString();
     */
    public String readString() {
        try {
            return readStringImpl();
        } catch (RemoteException e) {
            LOGGER.log(Level.FINE, CONTEXT, e);
        }
        return null;
    }

    private String readStringImpl() throws RemoteException {
        while (true) {
            if (communication.getRead() == 0 && SERVER.equals(communication.getSender())) {
                break;

            }
        }
        return communication.readMessage(ME);
    }

    /**
     * @See it.polimi.cg16.client.netAdapter#readInt();
     */
    public int readInt() {
        try {
            return readIntImpl();
        } catch (RemoteException e) {
            LOGGER.log(Level.FINE, CONTEXT, e);
        }
        return -1;
    }

    private int readIntImpl() throws RemoteException {
        while (true) {
            if (communication.getRead() == 0 && SERVER.equals(communication.getSender())) {
                break;
            }
        }

        return communication.readNumber(ME);
    }

    /**
     * @See it.polimi.cg16.client.netAdapter#sendMessage();
     */
    public void sendMessage(String msg) {
        try {
            sendMessageImpl(msg);
        } catch (RemoteException e) {
            LOGGER.log(Level.FINE, CONTEXT, e);
        }
    }

    private void sendMessageImpl(String msg) throws RemoteException {
        while (true) {
            if (communication.getRead() == 1) {
                break;
            }
        }
        communication.writeMessage(msg, ME);
    }

    /**
     * @See it.polimi.cg16.client.netAdapter#sendInt();
     */
    public void sendInt(int num) {
        try {
            sendIntImpl(num);
        } catch (RemoteException e) {
            LOGGER.log(Level.FINE, CONTEXT, e);
        }
    }

    private void sendIntImpl(int num) throws RemoteException {
        while (true) {
            if (communication.getRead() == 1) {
                break;
            }
        }
        communication.writeNumber(num, ME);
    }

}
